
using System;
using System.Linq;
using Xunit;
using Xunit.Extensions;


namespace Boilen.Primitives.Members {

    public class TestMember {

        [Fact]
        public void constructor_fails_for_null_name( ) {
            string nullName = null;

            Assert.Throws<ArgumentNullException>( ( ) => new MemberCtor( nullName ) );
        }

        [Fact]
        public void constructor_fails_for_empty_name( ) {
            string emptyName = "";

            Assert.Throws<ArgumentException>( ( ) => new MemberCtor( emptyName ) );
        }

        [Fact]
        public void constructor_succeeds_for_valid_arguments( ) {
            string name = "name";

            var member = new MemberCtor( name );

            Assert.Equal( member.Name, name );
        }


        [Fact]
        public void Empty_does_not_write_any_output( ) {
            var writer = new StringBuilderCodeWriter( );
            var member = Member.Empty;

            member.Write( writer );

            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, StringBuilderCodeWriter.EmptyLines );
        }


        [Fact]
        public void Write_fails_for_null_writer( ) {
            ICodeWriter nullWriter = null;
            var member = new MemberCtor( "name" );

            Assert.Throws<ArgumentNullException>( ( ) => member.Write( nullWriter ) );
        }


        [Fact]
        public void WriteMembers_fails_for_null_writer( ) {
            ICodeWriter nullWriter = null;
            Member[] members = new Member[0];

            Assert.Throws<ArgumentNullException>( ( ) => Member.WriteMembers( nullWriter, members ) );
        }

        [Fact]
        public void WriteMembers_fails_for_null_collection( ) {
            ICodeWriter writer = new StringBuilderCodeWriter( );
            Member[] nullMembers = null;

            Assert.Throws<ArgumentNullException>( ( ) => Member.WriteMembers( writer, nullMembers ) );
        }

        [Theory]
        [InlineData( 0, false )]
        [InlineData( 1, false )]
        [InlineData( 2, false )]
        [InlineData( 0, true )]
        [InlineData( 1, true )]
        [InlineData( 2, true )]
        public void WriteMembers_succeeds_for_member_collection_and_separator( int memberCount, bool useSeparator ) {
            string line = "test line";
            string separator = "!";

            var writer = new StringBuilderCodeWriter( );
            Action<int, bool> separatorMethod = null;
            if( useSeparator )
                separatorMethod = ( i, last ) => writer.Write( separator );

            string[] expectedLines = Enumerable.Range( 0, memberCount + 1 )
                .Select( i => useSeparator && i > 0 ? separator + line : line )
                .ToArray( );
            expectedLines[memberCount] = "";

            var members = Enumerable.Range( 0, memberCount )
                .Select( i => new WriteNameMember( line ) )
                .ToArray( );


            Member.WriteMembers( writer, members, separatorMethod );


            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, expectedLines );
        }

        [Fact]
        public void WriteMembers_inserts_newlines_between_groups( ) {
            string name = "name";
            string type = "type";
            string modifiers = "modifiers";
            string line = "test line";
            var members = new Member[] {
                new FieldMember( name, type ) { Modifiers = modifiers },
                new WriteNameMember( line )
            };
            var expectedLinesWriter = new StringBuilderCodeWriter( );
            members[0].Write( expectedLinesWriter );
            expectedLinesWriter.WriteLine( );
            members[1].Write( expectedLinesWriter );
            string[] expectedLines = expectedLinesWriter.GetLines( );

            var writer = new StringBuilderCodeWriter( );

            Member.WriteMembers( writer, members );

            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, expectedLines );
        }

        [Fact]
        public void WriteMembers_combines_groups_with_the_same_condition( ) {
            string name = "name";
            string type = "type";
            string modifiers = "modifiers";
            string line = "test line";
            CompilationSymbol condition = CompilationSymbol.Silverlight;
            var members = new Member[] {
                new FieldMember( name, type ) { Modifiers = modifiers, Condition = condition },
                new WriteNameMember( line ) { Condition = condition }
            };
            var expectedLinesWriter = new StringBuilderCodeWriter( );
            expectedLinesWriter.WriteLineUnindented( "#if {0}", condition.Symbol );
            members[0].Write( expectedLinesWriter );
            members[1].Write( expectedLinesWriter );
            expectedLinesWriter.WriteLineUnindented( "#endif" );
            string[] expectedLines = expectedLinesWriter.GetLines( );

            var writer = new StringBuilderCodeWriter( );

            Member.WriteMembers( writer, members );

            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, expectedLines );
        }

        [Theory]
        [InlineData( true )]
        [InlineData( false )]
        public void WriteMembers_only_inserts_newlines_between_members_when_SpaceOutput_is_true( bool shouldSpaceOutput ) {
            string line = "test line";
            var members = new Member[] {
                new WriteNameMember( line ) { SpaceOutput = shouldSpaceOutput },
                new WriteNameMember( line ) { SpaceOutput = shouldSpaceOutput },
                new WriteNameMember( line ) { SpaceOutput = shouldSpaceOutput }
            };
            int expectedLinesCount = shouldSpaceOutput
                ? members.Length * 2
                : members.Length + 1;
            string[] expectedLines = Enumerable.Range( 0, expectedLinesCount )
                .Select( i => shouldSpaceOutput && i % 2 == 1 ? "" : line )
                .ToArray( );
            expectedLines[expectedLinesCount - 1] = "";

            var writer = new StringBuilderCodeWriter( );

            Member.WriteMembers( writer, members );

            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, expectedLines );
        }


        [Fact]
        public void WriteMemberDocumentation_fails_for_null_writer( ) {
            ICodeWriter nullWriter = null;
            Member[] members = new Member[0];

            Assert.Throws<ArgumentNullException>( ( ) => Member.WriteMemberDocumentation( nullWriter, members ) );
        }

        [Fact]
        public void WriteMemberDocumentation_fails_for_null_collection( ) {
            ICodeWriter writer = new StringBuilderCodeWriter( );
            Member[] nullMembers = null;

            Assert.Throws<ArgumentNullException>( ( ) => Member.WriteMemberDocumentation( writer, nullMembers ) );
        }

        [Theory]
        [InlineData( 0 )]
        [InlineData( 1 )]
        [InlineData( 2 )]
        public void WriteMemberDocumentation_writes_expected_output_for_member_collection( int memberCount ) {
            var writer = new StringBuilderCodeWriter( );
            string line = "/// <inheritdoc/>";
            Doc doc = new Doc( "fqn", "name", "type", null );
            doc.InheritFrom = "";

            string[] expectedLines = Enumerable.Repeat( line, memberCount + 1 ).ToArray( );
            expectedLines[memberCount] = "";

            var members = Enumerable.Range( 0, memberCount )
                .Select( i => new WriteNameMember( i.ToString( ) ) { Doc = doc } )
                .ToArray( );


            Member.WriteMemberDocumentation( writer, members );


            string[] lines = writer.GetLines( );
            AssertExtensions.EqualCollection( lines, expectedLines );
        }


        #region Utility

        private sealed class MemberCtor : Member {
            public MemberCtor( string name ) : base( name ) { }
            public override Extensions UsedExtensions { get { return Extensions.None; } }
            protected override void WriteCore( ICodeWriter writer ) { }
        }

        #endregion

    }

}
